"""风险评估模型模块.

本模块实现交易风险评估功能:

1. 风险等级定义
   - 低风险 (LOW)
   - 中风险 (MEDIUM)
   - 高风险 (HIGH)
   - 严重风险 (CRITICAL)

2. 风险评估维度
   - 交易金额风险
   - 交易频率风险
   - 行为风险

3. 异常检测
   - 基于孤立森林算法
   - 多维度特征分析
   - 实时预警
"""

from enum import Enum
from typing import Dict, List, Optional, Tuple

import numpy as np
import pandas as pd
from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler

from ..utils.logger import logger


class RiskLevel(str, Enum):
    """风险等级枚举类."""
    
    LOW = "low"           # 低风险
    MEDIUM = "medium"     # 中风险
    HIGH = "high"         # 高风险
    CRITICAL = "critical" # 严重风险


class RiskModel:
    """风险评估模型类."""
    
    def __init__(self):
        """初始化风险模型.
        
        组件:
        1. StandardScaler: 特征标准化
        2. IsolationForest: 异常检测模型
        """
        self.scaler = StandardScaler()
        self.anomaly_detector = IsolationForest(
            contamination=0.1,  # 预期异常比例
            random_state=42     # 随机种子
        )
    
    def assess_transaction_risk(
        self,
        transaction: Dict[str, any]
    ) -> Tuple[RiskLevel, float, Dict[str, float]]:
        """评估单笔交易的风险.
        
        Args:
            transaction: 交易详情
            
        Returns:
            Tuple[RiskLevel, float, Dict[str, float]]:
                风险等级、风险分数、风险因素详情
        """
        try:
            # 提取特征
            features = self._extract_transaction_features(transaction)
            
            # 计算各维度风险分数
            amount_risk = self._calculate_amount_risk(
                amount=transaction.get("amount", 0),
                user_avg_amount=transaction.get("user_avg_amount", 0)
            )
            
            frequency_risk = self._calculate_frequency_risk(
                user_hourly_count=transaction.get("user_hourly_count", 0),
                user_daily_count=transaction.get("user_daily_count", 0)
            )
            
            behavior_risk = self._calculate_behavior_risk(
                user_risk_score=transaction.get("user_risk_score", 0),
                merchant_risk_score=transaction.get("merchant_risk_score", 0)
            )
            
            # 汇总风险因素
            risk_factors = {
                "amount_risk": amount_risk,        # 金额风险
                "frequency_risk": frequency_risk,  # 频率风险
                "behavior_risk": behavior_risk     # 行为风险
            }
            
            # 计算总体风险
            overall_risk = self._calculate_overall_risk(risk_factors)
            risk_level = self._determine_risk_level(overall_risk)
            
            return risk_level, overall_risk, risk_factors
            
        except Exception as e:
            logger.error(f"评估交易风险时发生错误: {e}")
            raise
    
    def detect_anomalies(
        self,
        transactions: pd.DataFrame,
        threshold: float = 0.95
    ) -> Tuple[pd.DataFrame, pd.DataFrame]:
        """检测异常交易.
        
        Args:
            transactions: 交易数据
            threshold: 异常检测阈值
            
        Returns:
            Tuple[pd.DataFrame, pd.DataFrame]: 正常交易和异常交易
        """
        try:
            # 提取用于异常检测的特征
            features = self._prepare_features_for_anomaly_detection(transactions)
            
            # 特征标准化
            self.scaler.fit(features)
            normalized_features = self.scaler.transform(features)
            
            # 异常检测
            predictions = self.anomaly_detector.fit_predict(normalized_features)
            
            # 区分正常和异常交易
            normal_mask = predictions == 1
            anomaly_mask = predictions == -1
            
            normal_transactions = transactions[normal_mask]
            anomalous_transactions = transactions[anomaly_mask]
            
            return normal_transactions, anomalous_transactions
            
        except Exception as e:
            logger.error(f"检测异常交易时发生错误: {e}")
            raise
    
    def _extract_transaction_features(
        self,
        transaction: Dict[str, any]
    ) -> np.ndarray:
        """提取交易特征.
        
        Args:
            transaction: 交易详情
            
        Returns:
            np.ndarray: 特征数组
            
        特征列表:
        1. 交易金额
        2. 小时交易次数
        3. 日交易次数
        4. 用户风险分数
        5. 商家风险分数
        """
        features = [
            transaction.get("amount", 0),
            transaction.get("user_hourly_count", 0),
            transaction.get("user_daily_count", 0),
            transaction.get("user_risk_score", 0),
            transaction.get("merchant_risk_score", 0)
        ]
        return np.array(features).reshape(1, -1)
    
    def _calculate_amount_risk(
        self,
        amount: float,
        user_avg_amount: float
    ) -> float:
        """计算金额风险.
        
        Args:
            amount: 当前交易金额
            user_avg_amount: 用户平均交易金额
            
        Returns:
            float: 金额风险分数(0-1)
        """
        if user_avg_amount == 0:
            return 0.5  # 新用户默认风险
        
        # 计算偏离度
        deviation = abs(amount - user_avg_amount) / user_avg_amount
        
        # 转换为风险分数(0-1)
        return min(1.0, deviation / 3.0)  # 超过3倍差异视为高风险
    
    def _calculate_frequency_risk(
        self,
        user_hourly_count: int,
        user_daily_count: int
    ) -> float:
        """计算频率风险.
        
        Args:
            user_hourly_count: 用户小时交易次数
            user_daily_count: 用户日交易次数
            
        Returns:
            float: 频率风险分数(0-1)
        """
        # 小时频率风险
        hourly_risk = min(1.0, user_hourly_count / 10.0)  # 每小时超过10次为高风险
        
        # 日频率风险
        daily_risk = min(1.0, user_daily_count / 50.0)  # 每天超过50次为高风险
        
        # 组合风险(小时权重0.7,日权重0.3)
        return 0.7 * hourly_risk + 0.3 * daily_risk
    
    def _calculate_behavior_risk(
        self,
        user_risk_score: float,
        merchant_risk_score: float
    ) -> float:
        """计算行为风险.
        
        Args:
            user_risk_score: 用户风险分数(0-100)
            merchant_risk_score: 商家风险分数(0-100)
            
        Returns:
            float: 行为风险分数(0-1)
        """
        # 将风险分数转换为0-1范围
        user_risk = 1 - (user_risk_score / 100.0)
        merchant_risk = 1 - (merchant_risk_score / 100.0)
        
        # 组合风险(用户权重0.6,商家权重0.4)
        return 0.6 * user_risk + 0.4 * merchant_risk
    
    def _calculate_overall_risk(
        self,
        risk_factors: Dict[str, float]
    ) -> float:
        """计算总体风险.
        
        Args:
            risk_factors: 各维度风险因素
            
        Returns:
            float: 总体风险分数(0-1)
        """
        # 风险权重配置
        weights = {
            "amount_risk": 0.4,      # 金额风险权重
            "frequency_risk": 0.3,   # 频率风险权重
            "behavior_risk": 0.3     # 行为风险权重
        }
        
        # 计算加权风险分数
        overall_risk = 0.0
        for factor, score in risk_factors.items():
            overall_risk += score * weights.get(factor, 0)
            
        return overall_risk
    
    def _determine_risk_level(self, risk_score: float) -> RiskLevel:
        """确定风险等级.
        
        Args:
            risk_score: 风险分数(0-1)
            
        Returns:
            RiskLevel: 风险等级
            
        风险等级划分:
        - 0.0-0.3: 低风险
        - 0.3-0.6: 中风险
        - 0.6-0.8: 高风险
        - 0.8-1.0: 严重风险
        """
        if risk_score < 0.3:
            return RiskLevel.LOW
        elif risk_score < 0.6:
            return RiskLevel.MEDIUM
        elif risk_score < 0.8:
            return RiskLevel.HIGH
        else:
            return RiskLevel.CRITICAL
    
    def _prepare_features_for_anomaly_detection(
        self,
        transactions: pd.DataFrame
    ) -> np.ndarray:
        """准备用于异常检测的特征.
        
        Args:
            transactions: 交易数据
            
        Returns:
            np.ndarray: 特征矩阵
            
        特征列表:
        1. 交易金额
        2. 小时交易次数
        3. 日交易次数
        4. 用户风险分数
        5. 商家风险分数
        """
        feature_columns = [
            "amount",
            "user_hourly_count",
            "user_daily_count",
            "user_risk_score",
            "merchant_risk_score"
        ]
        
        return transactions[feature_columns].values